use std::{
    thread,
    time::Duration,
    sync::mpsc,
};

pub struct Schedule;

impl Schedule {
    /// 通过线程创建定时器
    /// 该定时器特征为 传入的方法每次都会新开一个携程以执行
    /// 好处是 无论传入的方法执行时间多少 都会在第一时间执行
    /// 坏处是 每次都会新开与关闭携程 消耗性能
    /// 适用于 执行任务时间较长的情况
    ///
    /// `init_sec` 初始的时间 s
    /// `sleep_sec` 间隔的时间 s
    /// `func` 定时的方法
    ///
    /// ```rust
    /// use jormungand::basic::util::time::Schedule;
    /// Schedule::by_thread(0, 10, |t| { println!("{}", t) }).await;
    /// ```
    pub async fn by_thread<F>(init_sec: usize, sleep_sec: u64, func: F)
        where F: Fn(usize) + Send + Copy + Sync + 'static
    {
        tokio::spawn(async move {
            let mut current = init_sec;
            loop {
                tokio::spawn(async move { func(current); }).await.unwrap();
                thread::sleep(Duration::new(sleep_sec, 0));
                current += sleep_sec as usize;
            }
        }).await.unwrap();
    }

    /// 通过信道创建定时器
    /// 该定时器的特征是 传入的方法会在一个线程中等待定时线程的信道传入
    /// 好处是 无论执行任务的时长 都不会额外占用性能
    /// 坏处是 潜在的不够灵敏 如果执行任务时间超过定时间隔 会逐渐堆积信道
    /// 适用于 短时间执行的任务的情况 至少 `spend_sec` < `sleep_sec`
    ///
    /// `init_sec` 初始的时间 s
    /// `sleep_sec` 间隔的时间 s
    /// `func` 定时的方法
    ///
    /// ```rust
    /// use jormungand::basic::util::time::Schedule;
    /// Schedule::by_channel(0, 10, |t| { println!("{}", t) });
    /// ```
    pub fn by_channel<F>(init_sec: usize, sleep_sec: u64, func: F)
        where F: Fn(usize) + Send + Copy + Sync + 'static
    {
        thread::spawn(move || {
            let (sender, receiver) = mpsc::channel::<usize>();
            thread::spawn(move || {
                let mut current = init_sec;
                loop {
                    if let Err(e) = sender.send(current) {
                        println!("ScheduleError : {}", e);
                        break;
                    };
                    thread::sleep(Duration::new(sleep_sec, 0));
                    current += sleep_sec as usize;
                }
            });
            loop {
                match receiver.recv() {
                    Ok(current) => { func(current); }
                    Err(e) => {
                        println!("ScheduleError : {}", e);
                        break;
                    }
                };
            }
        });
    }
}
